package com.study.messagebus.core

import android.util.ArrayMap
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import com.study.messagebus.Receive
import java.lang.reflect.Method

/**
 * create: Zhang
 * 用于页面间的通信
 * 1.即时通信
 * 2.延迟通信:延迟发送数据，两种情况，A->B A发送B接收 B<-A B返回A B发送消息给A
 *
 * 注意:
 * 1.延迟消息分两种情况
 * ----如果消息发送的时候,在methodPool没有消费这条消息的方法,消息缓存,等待消费的方法出现,这种情况消息只能被消费一次，不支持一个发送多个接收
 * ----如果延迟消息发送的时候,在methodPool中有可消费的方法,则按即时消息发送
 * 2.即时消息可以有多个接收,但是在同一个注册类中只能被一个接收器消费
 */
object MessageBus {

    //接收消息的方法池
    private val methodPool = ArrayMap<LifecycleOwner, List<Method>>()

    //接收消息的消息池,key为消息的tag，池子里的消息处理后就remove
    private val messagePool = ArrayMap<String, Map<String, String>>()


    /**
     * isImmediately:为true消息发送即时接收 为false消息发送只有当接收的activity和fragment处于前台活动时接收
     * 注意：不要发送一个永远不会被消费的消息
     */
    fun send(tag: String, msg: Map<String, String>, delayed: Boolean = false) {

        //首先执行即时消息发送，不管是否是延迟消息
        //如果是延迟消息，但是目前有可消费这条消息的方法存在，则按照即时消息进行处理
        //如果没有可消费这调消息的方法存在，缓存消息，直到被消费
        if (!sendImmediately(tag, msg) && delayed) {
            messagePool[tag] = msg
        }
    }

    /**
     * 注册
     */
    fun <T : LifecycleOwner> register(target: T) {
        if (methodPool.containsKey(target)) {
            return
        }
        registerMethod(target)
        //监听target生命周期，生命周期结束自动取消注册
        target.lifecycle.addObserver(object : LifecycleEventObserver {
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                when (event) {
                    Lifecycle.Event.ON_DESTROY -> {
                        unregister(target)
                    }
                    Lifecycle.Event.ON_RESUME -> {
                        sendDelayed(target)
                    }
                    else -> {}
                }
            }
        })
    }

    /**
     * 取消注册
     */
    private fun <T : LifecycleOwner> unregister(target: T) {
        if (methodPool.containsKey(target)) {
            methodPool.remove(target)
        }
    }

    /**
     * 注册接收消息的方法
     */
    private fun <T : LifecycleOwner> registerMethod(target: T) {
        //获取当前类的所有方法，包括私有方法
        val methods = target::class.java.declaredMethods
        //一个类可能注册了多个接收器
        val methodList = mutableListOf<Method>()
        for (method in methods) {
            //拿到被注解的方法
            if (method.isAnnotationPresent(Receive::class.java)) {
                methodList.add(method)
            }
        }

        methodPool[target] = methodList
    }

    /**
     * 查询单个注册MessageBus的类的接收消息的方法，在消息池里面有没有对应的消息
     * 如果有对应的消息，处理消息
     *
     * method: 接收消息的方法
     * target: 注册消息接收器的类
     * tag: 消息的标签
     * msg: 消息
     */
    private fun dispatchMessage(
        method: Method,
        target: LifecycleOwner,
        tag: String,
        msg: Map<String, String>
    ): Boolean {
        //拿到方法上的注解元素
        val receive = method.getAnnotation(Receive::class.java)
        //对暗号，为了实现点对点的通信，发消息和接消息需要进行tag匹配
        if (receive!!.value == tag) {
            //开启访问权限
            method.isAccessible = true
            val types: Array<Class<*>> = method.parameterTypes
            if (types.size == 1 && types[0].name == "java.util.Map") {
                method.invoke(target, msg)
                return true
            } else {
                throw  Exception("接收器方法的入参必须有且只有一个Map<String,String>类的参数:${tag}")
            }
        }

        return false
    }

    /**
     * 延迟发送数据，两种情况，A->B A发送B接收 B<-A B返回A B发送消息给A
     */
    private fun sendDelayed(target: LifecycleOwner) {
        //获取当前类的所有方法，包括私有方法
        val methods = methodPool[target]
        //查看自己有没有方法可以处理这条消息
        methods?.forEach { method ->
            //拿到被注解的实例
            val receive = method.getAnnotation(Receive::class.java)
            //对暗号，为了实现点对点的通信，发消息和接消息需要进行tag匹配
            if (messagePool.containsKey(receive!!.value)) {

                dispatchMessage(
                    method = method,
                    target = target,
                    tag = receive.value,
                    msg = messagePool[receive.value]!!
                )

                //消息被消费后就移除，一条延迟发送的消息只能被消费一次
                messagePool.remove(receive.value)
                return
            }
        }
    }

    /**
     * 即时消息发送，遍历所有已注册的方法，配对成功发送消息
     */
    private fun sendImmediately(tag: String, msg: Map<String, String>): Boolean {
        var sendSuc = false
        //寻找匹配的方法
        methodPool.forEach { entry ->
            for (method in entry.value) {
                //找到被注解的方法，且tag标签能对应上，不在往下寻找，所以每个页面如果定义了多个tag标签相同的注解方法
                //只会执行第一个
                if (dispatchMessage(method = method, target = entry.key, tag = tag, msg = msg)) {
                    sendSuc = true
                    break
                }
            }
        }

        return sendSuc
    }
}